package wci.intermediate.typeimpl;

import static wci.intermediate.typeimpl.TypeFormImpl.ENUMERATION;
import static wci.intermediate.typeimpl.TypeFormImpl.SCALAR;
import wci.intermediate.TypeForm;
import wci.intermediate.TypeSpec;
import wci.intermediate.symtabimpl.Predefined;

/**
 * <h1>TypeChecker</h1>
 * <p>执行语法检查</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class TypeChecker
{
    /**
     * 检查类型是否一个整数
     * @param type 被检查类型说明
     * @return true/false
     */
    public static boolean isInteger(TypeSpec type)
    {
        return (type != null) && (type.baseType() == Predefined.integerType);
    }

    /**
     *  检查二元操作符的两个类型都是否是整数
     * @param type1 第一个被检查的类型
     * @param type2 第二个被检查的类型
     * @return true/false
     */
    public static boolean areBothInteger(TypeSpec type1, TypeSpec type2)
    {
        return isInteger(type1) && isInteger(type2);
    }

    /**
     * 检查类型是否一个实数，即计算机里面的浮点数
     * @param type 被检查类型
     * @return true/false
     */
    public static boolean isReal(TypeSpec type)
    {
        return (type != null) && (type.baseType() == Predefined.realType);
    }

    /**
     * 检查类型是否是整数或者实数，也就是一个实数系内的数
     * @param type 被检查类型
     * @return true/false
     */
    public static boolean isIntegerOrReal(TypeSpec type)
    {
        return isInteger(type) || isReal(type);
    }

    /**
     * 检查两个类型中至少有一个是实数，这个即计算机里面的数系扩大。比如INT+DOUBLE=DOUBLE
     * @param type1 第一个被检查类型
     * @param type2 第二个被检查类型
     * @return true/false
     */
    public static boolean isAtLeastOneReal(TypeSpec type1, TypeSpec type2)
    {
        return (isReal(type1) && isReal(type2)) ||
               (isReal(type1) && isInteger(type2)) ||
               (isInteger(type1) && isReal(type2));
    }

    /**
     * 检查类型是否布尔类型
     * @param type 被检查类型
     * @return true/false
     */
    public static boolean isBoolean(TypeSpec type)
    {
        return (type != null) && (type.baseType() == Predefined.booleanType);
    }

    /**
     * 检查参加布尔二元运算如&&,|| 的两个类型是否都是布尔数。
     * @param type1 第一个被检查类型
     * @param type2 第二个被检查类型
     * @return true/false
     */
    public static boolean areBothBoolean(TypeSpec type1, TypeSpec type2)
    {
        return isBoolean(type1) && isBoolean(type2);
    }

    /**
     * 检查类型是否一个字符类型
     * @param type 被检查类型
     * @return true/false
     */
    public static boolean isChar(TypeSpec type)
    {
        return (type != null) && (type.baseType() == Predefined.charType);
    }

    /**
     * 检查赋值两端的类型是否兼容，即左值是否能被右值兼容
     * @param targetType 左值类型
     * @param valueType 右值类型
     * @return true可以赋值/false不能赋值
     */
    public static boolean areAssignmentCompatible(TypeSpec targetType,
                                                  TypeSpec valueType)
    {
        if ((targetType == null) || (valueType == null)) {
            return false;
        }

        targetType = targetType.baseType();
        valueType  = valueType.baseType();

        boolean compatible = false;

        // 类型一样，ok
        if (targetType == valueType) {
            compatible = true;
        }

        // 左实数，右整数，ok
        else if (isReal(targetType) && isInteger(valueType)) {
            compatible = true;
        }

        // 字符串对字符串，赋值没问题
        else {
            compatible =
                targetType.isPascalString() && valueType.isPascalString();
        }

        return compatible;
    }

    /**
     * 检查两个类型是否能做到比较兼容，即a < b中的a/b可以兼容，如果a是INT，而b是String，这个就是不兼容的。
     * @param type1 比较操作符左端的类型
     * @param type2 比较操作符右端的类型
     * @return true/false
     */
    public static boolean areComparisonCompatible(TypeSpec type1,
                                                  TypeSpec type2)
    {
        if ((type1 == null) || (type2 == null)) {
            return false;
        }

        type1 = type1.baseType();
        type2 = type2.baseType();
        TypeForm form = type1.getForm();

        boolean compatible = false;

        // 两个标量或者枚举类型
        if ((type1 == type2) && ((form == SCALAR) || (form == ENUMERATION))) {
            compatible = true;
        }

        // 至少有一个是实数，另一个整数，没问题
        else if (isAtLeastOneReal(type1, type2)) {
            compatible = true;
        }

        // 两个字符串也可以比较
        else {
            compatible = type1.isPascalString() && type2.isPascalString();
        }

        return compatible;
    }
}
